/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const path = require('path');
const fs = require('fs');
const ts = require('typescript');
const ExcelJS = require('exceljs');

let inputDir = '';
let fileNeedChange = false;
let currentFileContent = '';
let currentFilePath = '';
const apiInfos = [];

const API_TYPE = new Map([
  [ts.SyntaxKind.ClassDeclaration, 'class'],
  [ts.SyntaxKind.StructDeclaration, 'struct'],
  [ts.SyntaxKind.InterfaceDeclaration, 'interface'],
  [ts.SyntaxKind.EnumDeclaration, 'enum_class'],
  [ts.SyntaxKind.EnumMember, 'enum_instance'],
  [ts.SyntaxKind.ModuleDeclaration, 'namespace'],
  [ts.SyntaxKind.MethodDeclaration, 'method'],
  [ts.SyntaxKind.MethodSignature, 'method'],
  [ts.SyntaxKind.FunctionDeclaration, 'method'],
  [ts.SyntaxKind.Constructor, 'method'],
  [ts.SyntaxKind.ConstructSignature, 'method'],
  [ts.SyntaxKind.CallSignature, 'method'],
  [ts.SyntaxKind.GetAccessor, 'method'],
  [ts.SyntaxKind.SetAccessor, 'method'],
  [ts.SyntaxKind.IndexSignature, 'method'],
  [ts.SyntaxKind.PropertyDeclaration, 'field'],
  [ts.SyntaxKind.PropertySignature, 'field'],
  [ts.SyntaxKind.VariableStatement, 'field'],
  [ts.SyntaxKind.AnnotationPropertyDeclaration, 'field'],
  [ts.SyntaxKind.TypeAliasDeclaration, 'field'],
  [ts.SyntaxKind.ExportAssignment, 'export'],
  [ts.SyntaxKind.ExportDeclaration, 'export'],
  [ts.SyntaxKind.OverloadDeclaration, 'overload'],
  [ts.SyntaxKind.AnnotationDeclaration, 'annotatiom'],
]);

const COMPILER_OPTIONS = {
  target: ts.ScriptTarget.ES2017,
  etsAnnotationsEnable: true,
};

collectDeclaration(process.argv[2]);

/**
 * 脚本入口
 * @param {string} interfacePath
 */
function collectDeclaration(interfacePath) {
  inputDir = interfacePath;
  try {
    const utFiles = [];
    const apiPath = path.resolve(inputDir, 'api');
    const arktsPath = path.resolve(inputDir, 'arkts');
    readFile(apiPath, utFiles);
    readFile(arktsPath, utFiles);
    tsTransform(utFiles, transformNode);
    exportApiInfos();
  } catch (error) {
    console.error('DELETE_SYSTEM_PLUGIN ERROR: ', error);
  }
}

/**
 * 导出文件
 */
async function exportApiInfos() {
  const workbook = new ExcelJS.Workbook();
  const sheet = workbook.addWorksheet('api差异', { views: [{ xSplit: 1 }] });
  sheet.getRow(1).values = [
    'apiId',
    'apiName',
    'apiType',
    'filePath',
    'parentName',
    'apiDefine',
    'staticVersion',
    'note',
    'newNote',
  ];
  for (let i = 0; i < apiInfos.length; i++) {
    let apiData = apiInfos[i];
    const filePath = '/interface/sdk-js/' + path.relative(inputDir, apiData.filePath).replace(/\\/g, '/');
    let apiId =
      apiData.apiName +
      '#' +
      apiData.apiType +
      '#' +
      filePath +
      '#' +
      apiData.parentName +
      '#' +
      apiData.apiDefine +
      '#' +
      apiData.staticVersion;
    const ADD_NUMBER = 2;
    sheet.getRow(i + ADD_NUMBER).values = [
      apiId,
      apiData.apiName,
      apiData.apiType,
      filePath,
      apiData.parentName,
      apiData.apiDefine,
      apiData.staticVersion,
      apiData.note,
      apiData.newNote,
    ];
  }
  const buffer = await workbook.xlsx.writeBuffer();
  fs.writeFileSync('./change_since.xlsx', buffer);
}

/**
 * 统一处理文件名称，修改后缀等
 * @param {string} filePath
 * @returns {string}
 */
function processFileName(filePath) {
  return path
    .basename(filePath)
    .replace(/\.d\.ts$/g, '.ts')
    .replace(/\.d\.ets$/g, '.ets');
}

/**
 * 遍历所有文件进行处理
 * @param {Array<string>} utFiles
 * @param {Function} callback
 */
function tsTransform(utFiles, callback) {
  utFiles.forEach(url => {
    if (!fs.existsSync(url)) {
      return;
    }
    const apiBaseName = path.basename(url);
    // 👇 关键：标准化换行符为 \n（解决 ^M 和空行问题）
    currentFileContent = fs.readFileSync(url, 'utf-8').replace(/\r\n/g, '\n');
    currentFilePath = url;
    fileNeedChange = false;

    let isTransformer = /\.d\.ts/.test(apiBaseName) || /\.d\.ets/.test(apiBaseName);
    if (/\.json/.test(url) || apiBaseName === 'index-full.d.ts') {
      isTransformer = false;
    }

    if (!isTransformer) {
      return;
    }

    const fileName = processFileName(url);
    ts.transpileModule(currentFileContent, {
      compilerOptions: COMPILER_OPTIONS,
      fileName: fileName,
      transformers: { before: [callback(url)] },
    });
  });
}

/**
 * 读取目录下所有文件
 * @param {string} dir
 * @param {Array<string>} utFiles
 */
function readFile(dir, utFiles) {
  try {
    const files = fs.readdirSync(dir);
    files.forEach(element => {
      const filePath = path.join(dir, element);
      const status = fs.statSync(filePath);
      if (status.isDirectory()) {
        readFile(filePath, utFiles);
      } else {
        utFiles.push(filePath);
      }
    });
  } catch (e) {
    console.log('ETS ERROR: ' + e);
  }
}

/**
 * 每个文件处理前回调函数
 * @param {string} url
 * @returns {Function}
 */
function transformNode(url) {
  return context => {
    return node => {
      sourceFile = node;
      node = processVisitEachChild(context, node);
      if (fileNeedChange) {
        writeFile(url, currentFileContent);
      }
      // 返回空 SourceFile 阻止默认 emit（因为我们只改注释）
      return ts.factory.createSourceFile([], ts.SyntaxKind.EndOfFileToken, ts.NodeFlags.None);
    };
  };
}

/**
 * 输出文件
 * @param {string} url
 * @param {string} data
 */
function writeFile(url, data) {
  const newFilePath = url;
  fs.mkdirSync(path.dirname(newFilePath), { recursive: true });
  if (data === '') {
    fs.rmSync(newFilePath, { force: true });
    return;
  }
  // 写回时保持 \n（Unix 风格），兼容 Git 和跨平台
  fs.writeFileSync(newFilePath, data, 'utf-8');
}

/**
 * 遍历节点
 * @param {ts.TransformationContext} context
 * @param {ts.Node} node
 * @returns {ts.Node}
 */
function processVisitEachChild(context, node) {
  return ts.visitEachChild(node, processAllNodes, context);

  function processAllNodes(node) {
    if (node.jsDoc && hasFinalStaticJSDoc(node.jsDoc)) {
      // 获取完整注释文本（不含代码）
      const fullText = node.getFullText();
      const codeText = node.getText();
      const jsDocText = replaceLastStr(fullText, codeText, '').trimEnd(); // 移除尾部空白

      // 按行分割
      const lines = jsDocText.split('\n');
      let changed = false;
      let staticVersion = '';

      // 从后往前找，只处理最后一个 @since ... dynamic&static
      for (let i = lines.length - 1; i >= 0; i--) {
        const line = lines[i];
        const match = line.match(/^(\s*\*\s*@since\s+)(\d+)(\s+dynamic&static)\s*$/);
        if (match) {
          const num = Number(match[2]);
          if (num < 23) {
            staticVersion = String(num);
            changed = true;
            console.error('lhmmm: ', lines);
            // 替换为两行
            lines.splice(i, 1, 
              `${match[1]}${num} dynamic`,
              `${match[1]}23 static`
            );
            
            console.error('lhm2222: ', lines);
            break; // 只处理最后一个
          }
        }
      }

      if (changed) {
        const newJsDocText = lines.join('\n');
        const apiInfo = {
          note: jsDocText,
          newNote: newJsDocText,
          staticVersion: staticVersion,
        };
        setApiInfo(apiInfo, node);
        apiInfos.push(apiInfo);
        fileNeedChange = true;
        currentFileContent = currentFileContent.replace(jsDocText, newJsDocText);
      }
    }

    return ts.visitEachChild(node, processAllNodes, context);
  }
}

// --- 以下辅助函数保持不变 ---

function setApiInfo(apiInfo, node) {
  apiInfo.apiType = API_TYPE.get(node.kind);
  apiInfo.apiName = getApiNodeName(node);
  apiInfo.filePath = currentFilePath;
  apiInfo.apiDefine = getApiDefine(node);
  apiInfo.parentName = getParentName(node);
  return apiInfo;
}

function getParentName(node) {
  let currentNode = node;
  let finalName = '';
  do {
    if (
      [
        ts.SyntaxKind.InterfaceDeclaration,
        ts.SyntaxKind.ClassDeclaration,
        ts.SyntaxKind.ModuleDeclaration,
        ts.SyntaxKind.EnumDeclaration,
        ts.SyntaxKind.StructDeclaration,
        ts.SyntaxKind.AnnotationDeclaration,
      ].includes(currentNode.kind)
    ) {
      finalName = '.' + getApiNodeName(currentNode) + finalName;
    }
    currentNode = currentNode.parent;
  } while (!ts.isSourceFile(currentNode));
  return finalName.replace(/^\./, '');
}

function getApiDefine(node) {
  let apiDefine = node.getText();
  if (node.modifiers) {
    let isDefine = false;
    node.modifiers.forEach(item => {
      if (ts.isDecorator(item)) {
        if (!isDefine) {
          apiDefine = node.getFullText();
          isDefine = true;
        }
        apiDefine = apiDefine.substring(item.end - item.pos);
      }
    });
  }
  if (apiDefine.includes('{')) {
    const tagIndex = apiDefine.indexOf('{');
    apiDefine = apiDefine.substring(0, tagIndex);
  }
  apiDefine = apiDefine.replace(/(;|,)\s*$/g, '').trim();
  return apiDefine;
}

function replaceLastStr(str, search, replace) {
  const lastIndex = str.lastIndexOf(search);
  if (lastIndex === -1) {
    return str;
  }
  return str.substring(0, lastIndex) + replace + str.substring(lastIndex + search.length);
}

function hasFinalStaticJSDoc(jsDocs) {
  const jsDoc = jsDocs[jsDocs.length - 1];
  if (jsDoc.tags) {
    return hasStaticSinceTag(jsDoc.tags);
  }
  return false;
}

function hasStaticSinceTag(tags) {
  for (let i = 0; i < tags.length; i++) {
    const tag = tags[i];
    if (tag.tagName.escapedText === 'since' && tag.comment.endsWith('static')) {
      return true;
    }
  }
  return false;
}

function getApiNodeName(node) {
  let apiName = '';
  if (ts.isVariableStatement(node)) {
    apiName = variableStatementGetEscapedText(node);
  } else if (ts.isExportAssignment(node)) {
    apiName = node.expression.escapedText;
  } else if (ts.isExportDeclaration(node)) {
    apiName = 'export default';
  } else if (ts.isIndexSignatureDeclaration(node)) {
    apiName = node.type.getText();
  } else if (ts.isConstructorDeclaration(node) || ts.isConstructSignatureDeclaration(node)) {
    apiName = 'constructor';
  } else {
    apiName = node.name?.getText();
  }
  return apiName;
}

function variableStatementGetEscapedText(statement) {
  let name = '';
  if (
    statement &&
    statement.declarationList &&
    statement.declarationList.declarations &&
    statement.declarationList.declarations.length > 0 &&
    statement.declarationList.declarations[0].name &&
    statement.declarationList.declarations[0].name.escapedText
  ) {
    name = statement.declarationList.declarations[0].name.escapedText.toString();
  }
  return name;
}